textrendering/textformatting/tagma/TmLine.cpp
changeset 32 8b9155204a54
parent 0 1fb32624e06b
child 40 91ef7621b7fc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/textformatting/tagma/TmLine.cpp	Fri Jun 04 10:37:54 2010 +0100
@@ -0,0 +1,1082 @@
+/*
+* Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+
+
+#include "TmLayoutImp.h"
+#include <s32mem.h> 
+#include "TmChunkInterp.h"
+#include "InlineText.h"
+#include "frmUtils.h"
+
+inline TBool IsSurrogate(TText a) { return 0xD800 == (a & 0xF800); }
+inline TBool IsHighSurrogate(TText a) { return 0xD800 == (a & 0xFC00); }
+inline TBool IsLowSurrogate(TText a) { return 0xDC00 == (a & 0xFC00); }
+inline TChar JoinSurrogates(TText aHigh, TText aLow)
+	{
+	return ((aHigh - 0xd7f7) << 10) + aLow;
+	}
+
+
+inline TText16 GetHighSurrogate(TUint aChar)
+	{
+	return STATIC_CAST(TText16, 0xD7C0 + (aChar >> 10));
+	}
+
+inline TText16 GetLowSurrogate(TUint aChar)
+	{
+	return STATIC_CAST(TText16, 0xDC00 | (aChar & 0x3FF));
+	}
+
+CTmLine::CTmLine():
+	iText(128),
+	iTextWithoutChunkOverlaps(128),
+	iChunk(32),
+	iRunInfo(32),
+	iCountedEndChar(0),
+	iHangingEndChar(0),
+	iEndChar(0),
+	iNextLineStartChar(0),	
+	iAtParEnd(FALSE),
+	iEllipsis(0xFFFF),
+	iEllipsisFormat(0),
+	iEllipsisWidth(0),
+	iContextChar(NULL)
+	{
+	}
+
+// Format a line using the information from a format context.
+void CTmLine::FormatL(CTmFormatContext& aContext)
+	{
+	// Reset arrays and parameters.
+	iChunk.Reset();
+	iText.Reset();
+	iTextWithoutChunkOverlaps.Reset();
+	iRunInfo.Reset();
+	iCountedEndChar = iHangingEndChar = iEndChar = iNextLineStartChar = 0;
+	iAtParEnd = FALSE;
+	iHeight = iAscent = 0;
+	iInnerBounds.SetRect(0,0,0,0);
+	iEllipsis = 0xFFFF;
+
+	// Determine the line break.
+	int start_x_coord = aContext.iLineInPar == 0 ? aContext.iFirstLineLeftMargin : aContext.iLeftMargin;
+	int align_x_coord = aContext.iParam.iWrapWidth -
+						(aContext.iLineInPar == 0 ? aContext.iFirstLineRightMargin : aContext.iRightMargin);
+	int wrap_x_coord = KMaxTInt;
+	if (aContext.iParam.IsWrapping() && !(aContext.iParFormat.iFlags & RTmParFormat::ENoWrap))
+		wrap_x_coord = align_x_coord;
+	BreakL(aContext,start_x_coord,wrap_x_coord);
+
+	// Calculate the ascent, descent and width.
+	TInt width;
+	TInt descent;
+	CalculateMetrics(aContext,width,iAscent,descent);
+
+	// Determine if the line has any border rules.
+	iBordered = aContext.iLeftBorder || aContext.iRightBorder ||
+				(aContext.iTopBorder && aContext.iLineInPar == 0) ||
+				(aContext.iBottomBorder && iAtParEnd);
+	
+	// Determine the next bidirectional category and next strong bidirectional category
+	aContext.iBdEndOfLine.Set(aContext.iTextCache, iNextLineStartChar);
+
+	/*
+	Calculate the bounds of the line. The outer bounds of the lines in a band completely tile
+	the band, and are a series of full-width horizontal slices; these are defined completely by their heights,
+	so iHeight represents the outer bounds. The inner bounds surround the actual text, not including margins,
+	indents and space above and below lines and paragraphs.
+	*/
+	iInnerBounds.SetRect(start_x_coord,0,start_x_coord + width,KMaxTInt);
+	if (aContext.iLineInPar == 0)
+		{
+		iInnerBounds.iTl.iY += aContext.iSpaceBefore;
+		if (iBordered && aContext.iTopBorder)
+			iInnerBounds.iTl.iY += aContext.iVBorderMargin + aContext.iTopBorder;
+		}
+	iHeight = iInnerBounds.iBr.iY = iInnerBounds.iTl.iY + iAscent + descent;
+	if (iAtParEnd)
+		{
+		iHeight += aContext.iSpaceAfter;
+		if (iBordered && aContext.iBottomBorder)
+			iHeight += aContext.iVBorderMargin + aContext.iBottomBorder;
+		}
+
+	// Perform right alignment, centring or full justification.
+	int measure = align_x_coord - start_x_coord;
+	switch (aContext.iParFormat.iAlignment)
+		{
+		case RTmParFormat::EAlignNormalBidirectional:
+			if (aContext.iParFormat.RightToLeft())
+				{
+				if(measure - width < 0)
+					{
+					iInnerBounds.Move(0, 0);	
+					}
+					else
+					{
+					iInnerBounds.Move(measure - width, 0);		
+					}
+				}
+			break;
+		case RTmParFormat::EAlignReverseBidirectional:
+			if (!aContext.iParFormat.RightToLeft())
+				iInnerBounds.Move(measure - width, 0);
+			break;
+		case RTmParFormat::EAlignCenter:
+			iInnerBounds.Move((measure - width) / 2,0);
+			break;
+		case RTmParFormat::EAlignJustify:
+			if (iAtParEnd || measure <= width)
+				{
+				// Default to normal bidirectional for the last line of
+				// a paragraph or if the margins are broken.
+				if (aContext.iParFormat.RightToLeft())
+					iInnerBounds.Move(measure - width, 0);
+				}
+			else
+				Justify(measure - width);
+			break;
+		case RTmParFormat::EAlignAbsoluteLeft:
+			// default is left
+			break;
+		case RTmParFormat::EAlignAbsoluteRight:
+			iInnerBounds.Move(measure - width, 0);
+			break;
+		default:
+			break;
+		}
+	}
+
+// Find the line break.
+void CTmLine::BreakL(CTmFormatContext& aContext,TInt aStartXCoord,TInt aWrapXCoord)
+	{
+	aContext.iLineEndsInForcedLineBreak = EFalse;
+	TInt truncate_x_coord = aWrapXCoord;
+	TBool truncating_with_ellipsis = aContext.iParam.IsTruncatingWithEllipsis(); // ####################
+	if (truncating_with_ellipsis)
+		truncate_x_coord = aContext.iParam.iWrapWidth - aContext.iRightMargin;
+
+	// The following character positions are all relative to the document, not the line.
+	TInt start_pos = aContext.iInfo.iEndChar;	// line start
+	TInt truncate_pos_with_ellipsis = KMaxTInt;	// position of truncation, allowing room for an ellipsis
+	TInt truncate_pos = KMaxTInt;				// position of truncation
+	TInt wrap_pos = KMaxTInt;					// position at the full measure
+	TInt break_pos = KMaxTInt;					// legal break position
+
+	CTmLine::TEllipsisInfo ellipsisInfo;		// structure used to carry info back from AppendChunk(s)L
+	
+	// Fill the line up to the truncation width.
+	TInt cur_pos = start_pos;
+	TInt cur_x_coord = aStartXCoord;
+
+	TInt maxChar = aContext.iDocumentLength + 1;
+	
+	AppendChunksL(aContext, cur_pos, cur_x_coord, maxChar, truncate_x_coord, truncating_with_ellipsis, ellipsisInfo);
+	iEllipsisFormat = ellipsisInfo.iEllipsisFormat;
+	iEllipsisWidth = ellipsisInfo.iEllipsisWidth;
+	truncate_pos_with_ellipsis = cur_pos;
+	if (truncating_with_ellipsis)
+		{
+		AppendChunksL(aContext, cur_pos, cur_x_coord, maxChar, truncate_x_coord, EFalse, ellipsisInfo);
+		}
+	truncate_pos = cur_pos;
+
+	// Fill the line up to the wrap width if it is greater than the truncation width.
+	if (aWrapXCoord > truncate_x_coord)
+		{
+		AppendChunksL(aContext, cur_pos, cur_x_coord, maxChar, aWrapXCoord, EFalse, ellipsisInfo);
+		}
+	wrap_pos = cur_pos;
+
+	// If the line has not been ended by a paragraph break or forced line end find a possible break.
+	TBool line_break_found = FALSE;
+	if (ellipsisInfo.iAtLineEnd || ellipsisInfo.iAtParEnd)
+		{
+		break_pos = wrap_pos;
+		line_break_found = TRUE;
+		aContext.iLineEndsInForcedLineBreak = ETrue;
+		}
+	TBool get_line_break_forwards = FALSE;
+	TInt min_break_pos = start_pos + 1;
+	TInt hanging_break_pos = break_pos;
+	TInt break_pos_after_spaces = break_pos;
+	while (!line_break_found)
+		{
+		TInt max_break_pos = cur_pos;
+		TInt context_end_pos = Min(aContext.iDocumentLength + 1,
+			max_break_pos + MTmSource::ELineBreakContext);
+		
+		// Check if context_end_pos points to surrogate high part
+		// If it is, the line break cannot correct, then adjust context_end_pos
+		if ( cur_pos <= aContext.iDocumentLength )
+		    {
+            TTmCharFormat new_format;
+            TPtrC text;
+            aContext.iSource.GetText( cur_pos, text, new_format );
+            if ( context_end_pos > cur_pos && text.Length() > 1 &&
+                    IsHighSurrogate( text[ text.Length() - 1 ] ) )
+                {
+                --context_end_pos;
+                }
+		    }
+		
+		// Append line breaking context.
+		AppendChunksL(aContext, cur_pos, cur_x_coord, context_end_pos, KMaxTInt, EFalse, ellipsisInfo);
+		TInt chars_appended = cur_pos - max_break_pos;
+		TPtrC p(iTextWithoutChunkOverlaps.Ptr(),iTextWithoutChunkOverlaps.Length());
+		if (aContext.iSource.GetLineBreakL(p,start_pos,min_break_pos - start_pos,max_break_pos - start_pos,
+										   get_line_break_forwards,break_pos,hanging_break_pos,break_pos_after_spaces))	
+			{
+			if (hanging_break_pos > iTextWithoutChunkOverlaps.Length() || break_pos_after_spaces > iTextWithoutChunkOverlaps.Length())
+				TmPanic(EBadLineBreak);
+			break_pos += start_pos;
+			hanging_break_pos += start_pos;
+			break_pos_after_spaces += start_pos;
+			if (break_pos < min_break_pos || break_pos > max_break_pos)
+				TmPanic(EBadLineBreak);
+			if (hanging_break_pos < break_pos || break_pos_after_spaces < hanging_break_pos)
+				TmPanic(EBadLineBreak);
+			line_break_found = TRUE;
+			}
+		// If no break has been found and no more characters could be added, break here.
+		else if (chars_appended == 0)
+			{
+			break_pos = hanging_break_pos = break_pos_after_spaces = max_break_pos;
+			line_break_found = TRUE;
+			}
+
+		/*
+		If no line break has been found, and if we prefer to break at an illegal position rather
+		than exceeding the measure, break at the truncation position but make sure the line
+		contains at least one character.
+		*/
+		if (!line_break_found && !aContext.iParam.LegalLineBreaksOnly())
+			{
+			break_pos = hanging_break_pos = break_pos_after_spaces = Max(wrap_pos,min_break_pos);
+			line_break_found = TRUE;
+			}
+
+		/*
+		After the first iteration, switch to searching forwards for a line break, because we know that the
+		text is now too wide and we want the shortest possible overlength line.
+		*/
+		get_line_break_forwards = TRUE;
+		min_break_pos = max_break_pos;
+		}
+
+	if (truncating_with_ellipsis && break_pos > truncate_pos)
+		{
+		iCountedEndChar = iHangingEndChar = iEndChar = truncate_pos_with_ellipsis;
+		iEllipsis = aContext.iParam.iEllipsis;
+		}
+	else
+		{
+		iCountedEndChar = break_pos;
+		iHangingEndChar = hanging_break_pos;
+		iEndChar = break_pos_after_spaces;
+		}
+
+	// Determine whether the line ends at the end of the paragraph.
+	iAtParEnd = break_pos_after_spaces == ellipsisInfo.iParEndPos;
+	if (iAtParEnd)
+		aContext.iLineEndsInForcedLineBreak = ETrue;
+
+	iNextLineStartChar = break_pos_after_spaces;
+
+	/*
+	Truncate the text after the displayed characters, ensuring there is a break of chunk at this point.
+	This makes it easier to justify the line and write the bytecode.
+	*/
+	if (iCountedEndChar < cur_pos)
+		{
+		int chunk_start_pos = start_pos;
+		int chunk_end_pos = start_pos;
+		int chunk_index = 0;
+		cur_x_coord = aStartXCoord;
+		// find the chunk that contains the end of the text,(iCountedEndChar)
+		while (chunk_index < iChunk.Length())
+			{
+			chunk_end_pos += (iChunk[chunk_index].iTextLength - iChunk[chunk_index].iOverlappingChars);
+			if (chunk_end_pos > iCountedEndChar)
+				break;
+			cur_x_coord += iChunk[chunk_index].iWidth;
+			chunk_start_pos = chunk_end_pos;
+			chunk_index++;
+			}
+		//  remove the chunk that contains the last bit of text and any after
+		iChunk.Truncate(chunk_index);
+		iRunInfo.Truncate(chunk_index);
+		iText.Truncate(chunk_start_pos - start_pos);
+		cur_pos = chunk_start_pos;
+
+		// add a chunks so there is just enough text to reach the end
+		while (cur_pos < iCountedEndChar)
+			{
+			if (!AppendChunkL(aContext,cur_pos,cur_x_coord,iCountedEndChar,KMaxTInt,FALSE,ellipsisInfo))
+				break;
+			}
+		while (cur_pos < iHangingEndChar)
+			{
+			if (!AppendChunkL(aContext,cur_pos,cur_x_coord,iHangingEndChar,KMaxTInt,FALSE,ellipsisInfo))
+				break;
+			}
+			
+		// If the last chunk is a soft hyphen restore its full width.		
+		if (iChunk.Length())
+			{
+			TTmChunk& last_chunk = iChunk[iChunk.Length() - 1];
+			if (last_chunk.iType == TTmChunk::ESoftHyphenChunk)
+				last_chunk.iWidth = last_chunk.iStandardWidth;
+			}
+
+		}
+	}
+
+// Determine the ascent, descent and width of a line.
+void CTmLine::CalculateMetrics(CTmFormatContext& aContext,TInt& aWidth,TInt& aAscent,TInt& aDescent) const
+	{
+	aWidth = 0;
+	aAscent = 0;
+	aDescent = 0;
+	MTmSource::TLineHeightParam p;
+	p.iFontMaxAscent = aContext.iBulletAscent;
+	p.iFontMaxDescent = aContext.iBulletDescent;
+	int i = 0;
+	const TTmChunk* c = NULL;
+	for (i = 0, c = iChunk.Ptr(); i < iChunk.Length(); i++, c++)
+		{
+		if (c->iType == TTmChunk::EPictureChunk)
+			{
+			switch (c->iFormat.iPictureAlignment)
+				{
+				case TTmCharFormat::EPictureAlignTop:
+					if (c->iMaxCharHeight > p.iMaxTopPictureHeight)
+						p.iMaxTopPictureHeight = c->iMaxCharHeight;
+					break;
+				case TTmCharFormat::EPictureAlignBottom:
+					if (c->iMaxCharHeight > p.iMaxBottomPictureHeight)
+						p.iMaxBottomPictureHeight = c->iMaxCharHeight;
+					break;
+				case TTmCharFormat::EPictureAlignCenter:
+					if (c->iMaxCharHeight > p.iMaxCenterPictureHeight)
+						p.iMaxCenterPictureHeight = c->iMaxCharHeight;
+					break;
+				case TTmCharFormat::EPictureAlignBaseline:
+					if (c->iMaxCharHeight > p.iMaxCharHeight)
+						p.iMaxCharHeight = c->iMaxCharHeight;
+					break;
+				}
+			}
+		else
+			{
+			// Need to include the baseline offset in the Ascent/Decent 
+			// calculations in case the font for this chunk is a 
+			// superscript/subscript font. Offset will be 0 for a normal font.
+			// Greatest Ascent = iAscent+ABS(iBaselineOffset)
+			// Greatest Descent = MAX(p.iFontMaxDescent, c->iBaselineOffset)
+			// See defect INC000926.
+			if ((c->iAscent-c->iBaselineOffset) > p.iFontMaxAscent)
+				p.iFontMaxAscent = c->iAscent - c->iBaselineOffset;
+			if ((c->iDescent > p.iFontMaxDescent) || (c->iBaselineOffset > p.iFontMaxDescent))
+				p.iFontMaxDescent = Max(c->iDescent, c->iBaselineOffset);
+
+			if (c->iMaxCharHeight > p.iMaxCharHeight)
+				p.iMaxCharHeight = c->iMaxCharHeight;
+			if (c->iMaxCharDepth > p.iMaxCharDepth)
+				p.iMaxCharDepth = c->iMaxCharDepth;
+			if (c->iFontMaxHeight > p.iFontMaxCharHeight)
+				p.iFontMaxCharHeight = c->iFontMaxHeight;
+			if (c->iFontMaxDepth > p.iFontMaxCharDepth)
+				p.iFontMaxCharDepth = c->iFontMaxDepth;
+			}
+		aWidth += c->iWidth;
+		}
+	if (iEllipsis != 0xFFFF)
+		aWidth += iEllipsisWidth;
+	
+	// If the line has no characters set the ascent and descent to those of the current font.
+	if (!iChunk.Length() || !(p.iFontMaxAscent || p.iFontMaxDescent))
+		{
+		TPtrC text;
+		CTmTextFontCache* font = NULL;
+		aContext.iTextCache.GetText(aContext.iInfo.iEndChar,KMaxTInt,text,NULL,&font);
+		if (font)
+			{
+			// Changes for defects INC000922 & DEF000925. 
+			// If a bullet point is present at the start of an empty paragraph
+			// which is bigger than the text font size then we need to preserve
+			// the bullet point sizes set above over the font sizes. This is
+			// why IFs surround the two assignments below. 
+			if (font->Font().AscentInPixels() > p.iFontMaxAscent)
+				p.iFontMaxAscent = font->Font().AscentInPixels();
+			if (font->Font().DescentInPixels() > p.iFontMaxDescent)
+				p.iFontMaxDescent = font->Font().DescentInPixels();
+			/* get the MaxCharHeight and MaxCharDepth */
+			GetMaxHeightAndDepth(&font->Font(), p.iFontMaxCharHeight, p.iFontMaxCharDepth ); 
+			font->Close();
+			}
+		}
+
+	// Set the line's ascent and descent to take account of desired minimum line height, overlarge characters, etc.
+	p.iDesiredLineHeight = aContext.iLineSpacing;
+	p.iExactLineHeight = aContext.iExactLineSpacing;
+	aContext.iSource.SetLineHeight(p,aAscent,aDescent);
+	}
+
+void CTmLine::Justify(TInt aExtraSpace)
+	{
+	/*
+	Find the range to stretch: from the chunk after the last tab to the penultimate chunk;
+	but don't stretch if the last tab was not a standard tab.
+	*/
+	int last = iChunk.Length() - 1;
+	int first = last;
+	int i;
+	TBool non_standard_tab = FALSE;
+	for (i = last - 1; i >= 0; i--)
+		{
+		if (iChunk[i].iType & TTmChunk::ETabFlag)
+			{
+			if (iChunk[i].iType != TTmChunk::EStandardTabChunk)
+				non_standard_tab = TRUE;
+			break;
+			}
+		first = i;
+		}
+	if (first >= last || non_standard_tab)
+		return;
+
+	// Total the stretchability.
+	int stretch = 0;
+	TTmChunk* c;
+	for (i = first, c = iChunk.Ptr() + first; i < last; i++, c++)
+		stretch += c->iStretch;
+
+	// Do nothing if there's no stretchability.
+	if (stretch == 0)
+		return;
+
+	// Adjust the line bounds.
+	iInnerBounds.iBr.iX += aExtraSpace;
+
+	// Add space to the stretchable chunks in proportion to their stretchability.
+	while (aExtraSpace > 0)
+		{
+		int dividend = aExtraSpace;
+		for (i = first, c = iChunk.Ptr() + first; i < last && aExtraSpace > 0; i++, c++)
+			if (c->iStretch > 0)
+				{
+				c->iType = TTmChunk::EStretchedChunk;
+				int increment = (dividend * c->iStretch) / stretch;
+				if (increment == 0)
+					increment = 1;
+				if (increment > aExtraSpace)
+					increment = aExtraSpace;
+				c->iWidth += increment;
+				aExtraSpace -= increment;
+				}
+		}
+	}
+
+
+void CTmLine::AppendChunksL(CTmFormatContext& aContext,TInt& aStartChar,TInt& aStartXPos,TInt aMaxChar,TInt aMaxXPos,
+						      TBool aAllowSpaceForFinalInlineText,CTmLine::TEllipsisInfo& aEllipsisInfo)
+	{
+	while (aStartChar < aMaxChar && !aEllipsisInfo.iAtLineEnd && !aEllipsisInfo.iAtParEnd)
+		{
+		aEllipsisInfo.iEllipsisFormat = aStartChar;
+		if (!AppendChunkL(aContext, aStartChar, aStartXPos, aMaxChar, aMaxXPos, aAllowSpaceForFinalInlineText, aEllipsisInfo))
+			break;
+		}
+	if (aEllipsisInfo.iAtParEnd)
+		aEllipsisInfo.iParEndPos = aStartChar;
+	}
+
+
+TBool CTmLine::AppendChunkL(CTmFormatContext& aContext,TInt& aStartChar,TInt& aStartXPos,TInt aMaxChar,TInt aMaxXPos,
+						    TBool aAllowSpaceForFinalInlineText,CTmLine::TEllipsisInfo& aEllipsisInfo)
+	{
+	/*
+	Find any previous non-standard tab, so that we can adjust the measure to allow for characters pushed left.
+	Accumulate the segment width (width of chunks between tabs), which will be used to
+	adjust the width of the previous tab if necessary.
+
+	Return TRUE if it was possible to append a new chunk of non-zero length. It will be impossible if
+	aMaxChar is <= aStartChar or the chunk consists of a ligation that ends after aMaxChar, or the smallest thing
+	that can be appended is wider than aMaxXPos - aStartXPos.
+	*/
+	int segment_width = 0;
+	TTmChunk* prev_tab = NULL;
+
+	if (iChunk.Length() > 0)
+		{
+		int i = iChunk.Length() - 1;
+		TTmChunk* c = &iChunk[i];
+		while (i >= 0 && !prev_tab)
+			{
+			if (c->iType & TTmChunk::ETabFlag)
+				{
+				if (c->iType != TTmChunk::EStandardTabChunk)
+					{
+					prev_tab = c;
+					int avail = aMaxXPos - aStartXPos;
+					if (prev_tab->iType == TTmChunk::ECenterTabChunk)
+						aMaxXPos += Min(avail,prev_tab->iWidth);
+					else
+						aMaxXPos += prev_tab->iWidth;
+					}
+				break;
+				}
+			else
+				segment_width += c->iWidth;
+			c--;
+			i--;
+			}
+		}
+
+	//+ strip trailing whitespace when measuring centred segment?
+
+	// Prepare to zero the metrics of the preceding chunk if it was a soft hyphen.
+	int prev_soft_hyphen_width = 0;
+	if (iChunk.Length() && iChunk[iChunk.Length() - 1].iType == TTmChunk::ESoftHyphenChunk)
+		prev_soft_hyphen_width = iChunk[iChunk.Length() - 1].iWidth;
+
+	// Create the new chunk.
+	TTmChunk chunk;
+	TTmChunk::TInfo chunkInfo;
+	// If this is the first chunk of the line, then take the context  from the line. Otherwise, take
+	// it form the previous chunk in this line.
+	if (iChunk.Length() == 0)
+		chunk.iContextChar = iContextChar;
+	else
+		chunk.iContextChar = iChunk[iChunk.Length() - 1].iContextChar;
+	
+	chunk.SetL(aContext,aStartChar,aStartXPos - prev_soft_hyphen_width,aMaxChar,aMaxXPos,aAllowSpaceForFinalInlineText,chunkInfo);
+	// copy over info to be passed back
+	aEllipsisInfo.iEllipsisWidth = chunkInfo.iEllipsisWidth;
+	aEllipsisInfo.iAtLineEnd = chunkInfo.iAtLineEnd;
+	aEllipsisInfo.iAtParEnd = chunkInfo.iAtParEnd;
+	TBidirectionalState::TRunInfo run_info;
+	run_info.iCategory = chunkInfo.iBdCat;
+	run_info.iIndex = iChunk.Length();
+	
+	// Add the chunk and the bidirectional information to the arrays.
+	if (chunk.iTextLength > 0)
+		{
+		if (prev_soft_hyphen_width)
+			{
+			TTmChunk& c = iChunk[iChunk.Length() - 1];
+			c.iWidth = 0;
+			c.iAscent = 0;
+			c.iDescent = 0;
+			c.iMaxCharHeight = 0;
+			c.iMaxCharDepth = 0;
+			aStartXPos -= prev_soft_hyphen_width;
+			}
+
+		iChunk.AppendL(chunk);
+		int start = chunk.iDocPos;
+		int end = chunk.iDocPos + chunk.iTextLength;
+		TPtrC text;
+		while (start < end)
+			{
+			aContext.iTextCache.GetText(start,end,text);
+			iText.AppendL(text.Ptr(),text.Length());
+			iTextWithoutChunkOverlaps.AppendL(text.Ptr(),text.Length() - chunk.iOverlappingChars);
+			start += text.Length();
+			}
+
+		iRunInfo.AppendL(run_info);
+
+		aStartChar += chunk.iTextLength;
+		if (chunk.iOverlappingChars > 0 && ! chunkInfo.iAtLineEnd && ! chunkInfo.iAtParEnd)
+			{
+			aStartChar -= chunk.iOverlappingChars;
+			__ASSERT_DEBUG(aStartChar >= 0, TmPanic(EInvariant));
+			}
+
+		aStartXPos += chunk.iWidth;
+
+		// Adjust the width of any previous centring or right-aligning tab unless the new chunk is itself a tab
+		if (prev_tab && !(chunk.iType & TTmChunk::ETabFlag))
+			{
+			segment_width += chunk.iWidth;
+			int old_chunk_width = prev_tab->iWidth;
+			if (prev_tab->iType == TTmChunk::ECenterTabChunk)
+				prev_tab->iWidth = Max(0,prev_tab->iStandardWidth - segment_width / 2);
+			else
+				prev_tab->iWidth = Max(0,prev_tab->iStandardWidth - segment_width);
+			aStartXPos += prev_tab->iWidth - old_chunk_width;
+			}
+		if (chunkInfo.iTruncated)	// move test for truncation to here from loop around AppendChunkL
+			return EFalse;
+		return ETrue;
+		}
+	else
+		return EFalse;
+	}
+
+void CTmLine::WriteCodeL(CTmFormatContext& aContext)
+	{
+	TInt line_start_bytecode_pos = aContext.iCode.Size();
+	CTmCode& code = aContext.iCode;
+	code.CreateBufferL();
+	TInt doc_pos = aContext.iInfo.iEndChar;
+	
+	// Context for the line. From logic end chunk in this line
+	TUint lineContextChar = iChunk[iChunk.Length()-1].iContextChar;
+
+	// Write the bidirectional state in force at the start of the line.
+	if (!aContext.iBdState.IsDefault())
+		WriteBdStateL(aContext);
+
+	// Reorder the line. This changes the bidirectional state so has to be done after that is written.
+	TBool visualEndIsAmbiguous;
+	aContext.iBdState.ReorderLine(iRunInfo.Ptr(),iRunInfo.Length(),
+		aContext.iLineInPar == 0, AtParEnd(), aContext.iParFormat.RightToLeft(),
+		aContext.iBdEndOfLine.FirstCategory(),
+		aContext.iBdEndOfLine.FirstStrongCategory(),
+		visualEndIsAmbiguous);
+
+	// Create and write the paragraph label if any. Trap the operation so that label mode can be cancelled.
+	if (aContext.iLineInPar == 0)
+		{
+		MFormLabelApi* labelApi = (MFormLabelApi*)aContext.iSource.GetExtendedInterface(KFormLabelApiExtensionUid);
+		if ((labelApi != NULL) && (labelApi->LabelModeSelect(MTmSource::EParLabel, aContext.iInfo.iEndChar)))
+			{
+			TRAPD(error, WriteLabelCodeL(aContext,MTmSource::EParLabel));
+			labelApi->LabelModeCancel();
+			if (error)
+				User::Leave(error);
+			}
+		}
+
+	// Write out the termination characters if they are on the left because
+	// the paragraph is right to left.
+	TInt termination_chars = iEndChar - iHangingEndChar;
+	if (termination_chars > 0 && aContext.iParFormat.RightToLeft())
+		{
+		TUint8 op = static_cast<TUint8>(TTmInterpreter::EOpText
+			| TTmInterpreter::EModDontMeasure
+			| TTmInterpreter::EModRightToLeft);
+		if (iHangingEndChar != doc_pos)
+			op |= TTmInterpreter::EModPos;
+		if (termination_chars != 1)
+			op |= TTmInterpreter::EModCount;
+		code.AppendByteL(op);
+		if (op & TTmInterpreter::EModPos)
+			code.AppendNumberL(iHangingEndChar - doc_pos);
+		if (op & TTmInterpreter::EModCount)
+			code.AppendNumberL(termination_chars);
+		TInt width = aContext.iTextCache.TotalWidthL(iHangingEndChar, iEndChar, ETrue);
+		code.AppendNumberL(-width);
+		// Append context , which is NULL in this case
+		code.AppendNumberL(0);
+		doc_pos = iEndChar;
+		}
+	/*
+	Write the line in the order defined by iRunInfo.
+	Amalgamate adjacent text chunks with the same direction. This is a very worthwhile
+	optimisation to the size of the generated bytecode.
+	Chunks of type ETextChunk are pieces of text to be displayed at their natural widths, so can be concatenated.
+	*/
+	TInt group_start = 0;
+	TInt group_left = 0;
+	TInt group_right = 0;
+	TUint contextChar = 0;
+    TInt line_start_position = 0;
+    //Record current line start position which will be used to get line offset position of inlineText
+    if (iRunInfo.Length() > 0)
+        {
+        TInt first_group_start_index = iRunInfo[0].iIndex;
+        TTmChunk* first_group_start_chunk = &iChunk[first_group_start_index];
+        line_start_position = first_group_start_chunk->iDocPos;
+        }
+    
+	while (group_start < iRunInfo.Length())
+		{
+		TInt group_end = group_start + 1;
+
+		// Decide the base operator.
+		TInt group_start_index = iRunInfo[group_start].iIndex;
+		TInt direction = iRunInfo[group_start].iDirection;
+		TTmChunk* group_start_chunk = &iChunk[group_start_index];
+		TInt pos = group_start_chunk->iDocPos;
+		TInt length = group_start_chunk->iTextLength;
+		TInt initialInlineWidth = group_start_chunk->iInitialInlineWidth;
+		TInt finalInlineWidth = group_start_chunk->iFinalInlineWidth;
+		TBool forcedChunkEnd = group_start_chunk->iForcedChunkEnd;
+		contextChar = group_start_chunk->iContextCharInByteCode;
+		
+		TInt inlineFormat = pos >= line_start_position ?
+		    pos - line_start_position :
+            line_start_position - pos;//Position offset in current line
+		group_right += group_start_chunk->iWidth;
+		TInt subscript = 0;
+		TUint8 op = 0;
+		TBool skip_this_chunk = EFalse;
+		switch (group_start_chunk->iType)
+			{
+			case TTmChunk::ETextChunk:
+			case TTmChunk::EStretchedChunk:
+				op = TTmInterpreter::EOpText;
+				break;
+			case TTmChunk::ESoftHyphenChunk:
+				op = TTmInterpreter::EOpText;
+				if (group_start < iRunInfo.Length() - 1)
+					skip_this_chunk = ETrue;
+				break;
+			case TTmChunk::EStandardTabChunk:
+			case TTmChunk::ECenterTabChunk:
+			case TTmChunk::EReverseTabChunk:
+				op = TTmInterpreter::EOpText;
+				break;
+			case TTmChunk::EPictureChunk:
+				op = TTmInterpreter::EOpSpecialChar | TTmInterpreter::EModSubscript;
+				switch (group_start_chunk->iFormat.iPictureAlignment)
+					{
+					case TTmCharFormat::EPictureAlignTop:
+						subscript = group_start_chunk->iMaxCharHeight - iAscent;
+						break;
+					case TTmCharFormat::EPictureAlignBottom:
+						subscript = iInnerBounds.Height() - iAscent;
+						break;
+					case TTmCharFormat::EPictureAlignCenter:
+						subscript = (iInnerBounds.Height() + group_start_chunk->iMaxCharHeight) / 2 - iAscent;
+						break;
+					case TTmCharFormat::EPictureAlignBaseline:
+						subscript = 0;
+						break;
+					}
+				break;
+			default:
+				TmPanic(EBadChunkType);
+				break;
+			}
+
+		TBool ellipsis_needed = EFalse;
+		TInt overlappingCharacters = group_start_chunk->iOverlappingChars;
+
+		if (!skip_this_chunk)
+			{
+
+			// Amalgamate text chunks if possible.
+			if (aContext.iXScale == 1000 && group_start_chunk->iType == TTmChunk::ETextChunk)
+				while (group_end < iRunInfo.Length())
+					{
+					TInt group_end_index = iRunInfo[group_end].iIndex;
+					TTmChunk* group_end_chunk = &iChunk[group_end_index];
+					TInt index_difference = group_end - group_start;
+					if (direction == 1)
+						index_difference = -index_difference;
+					
+					// Check to see if the chunk cannot be amalgamated
+					if (group_end_index - group_start_index != index_difference ||		// test for adjacency,
+						iRunInfo[group_end].iDirection != direction ||					// same direction,
+						group_end_chunk->iType != TTmChunk::ETextChunk ||				// text type
+						group_end_chunk->iFormat != group_start_chunk->iFormat ||		// same format
+						// inline text insertion or side-bearings supress amalgamation
+						forcedChunkEnd || group_end_chunk->iForcedChunkStart ||
+						length + group_end_chunk->iTextLength > KMaxTextChunkSize)		// within maximum chunk length
+							{
+							break;
+							}
+						
+					if (direction == 0)
+						{
+						if (finalInlineWidth + group_end_chunk->iInitialInlineWidth != 0)
+							break;
+						}
+					else
+						{
+						if (initialInlineWidth + group_end_chunk->iFinalInlineWidth != 0)
+							break;
+						}
+					if (direction == 1)
+						pos = group_end_chunk->iDocPos;
+					
+					//Handle the length of chunks with overlapping characters ensuring
+					//that when the chunks are amalgamated, the length is corrected.
+					length += group_end_chunk->iTextLength - overlappingCharacters;
+					overlappingCharacters = group_end_chunk->iOverlappingChars;
+					
+					group_right += group_end_chunk->iWidth;
+					initialInlineWidth += group_end_chunk->iInitialInlineWidth;
+					finalInlineWidth += group_end_chunk->iFinalInlineWidth;
+					forcedChunkEnd = group_end_chunk->iForcedChunkEnd;
+					// If chunks are amalgamated, the context from the first chunk will provide context for the
+					// amalgamated chunks.
+					contextChar = group_start_chunk->iContextCharInByteCode;
+
+					group_end++;
+					}
+
+			// Scale the width and subscript if necessary.
+			TInt width = group_right - group_left;
+			if (aContext.iXScale != 1000)
+				{
+				width = (group_right * aContext.iXScale / 1000) -
+						(group_left * aContext.iXScale / 1000);
+				}
+			width -= (initialInlineWidth + finalInlineWidth);
+
+			if (aContext.iYScale != 1000)
+				subscript = subscript * aContext.iYScale / 1000;
+
+			// Write a leading ellipsis if this is the content-final chunk and is right-to-left.
+			ellipsis_needed = iEllipsis != 0xFFFF && iRunInfo[group_end - 1].iIndex == iChunk.Length() - 1;
+			if (ellipsis_needed && direction == 1)
+				{
+				code.AppendByteL(TTmInterpreter::EOpInlineText);
+				code.AppendNumberL(iEllipsisWidth);
+				if (iEllipsisFormat >= line_start_position)
+					code.AppendNumberL(iEllipsisFormat - line_start_position);
+				else
+					code.AppendNumberL(line_start_position - iEllipsisFormat);
+				code.AppendNumberL(iEllipsis);
+				}
+
+			// Is there any inline text to go here - before the chunk starts
+			if ((initialInlineWidth > 0) && (direction == 0))
+				{ // yes - go fetch it
+				WriteInlineTextL(aContext, code, pos, initialInlineWidth, ETrue, inlineFormat);
+				}
+			else if ((finalInlineWidth > 0) && (direction == 1))
+				{ // yes - go fetch it
+				WriteInlineTextL(aContext, code, pos + length, finalInlineWidth, EFalse, inlineFormat);
+				}
+
+			// Decide which optional arguments are needed and write the operator and its arguments.
+			if (pos != doc_pos)
+				op |= TTmInterpreter::EModPos;
+			if (length > 1)
+				op |= TTmInterpreter::EModCount;
+			if (direction == 1)
+				op |= TTmInterpreter::EModRightToLeft;
+			code.AppendByteL(op);
+			if (op & TTmInterpreter::EModPos)
+				code.AppendNumberL(pos - doc_pos);
+			if (op & TTmInterpreter::EModCount)
+				code.AppendNumberL(length);
+			if (op & TTmInterpreter::EModSubscript) // at present used only for pictures
+				code.AppendNumberL(subscript);
+			code.AppendNumberL(width);
+			code.AppendNumberL(contextChar);	// Append the context  for this chunk
+			
+			// Is there any inline text to go here - after the end of the chunk
+			if ((finalInlineWidth > 0) && (direction == 0))
+				{ // yes - go fetch it
+				WriteInlineTextL(aContext, code, pos + length, finalInlineWidth, EFalse, inlineFormat);
+				}
+			else if ((initialInlineWidth > 0) && (direction == 1))
+				{ // yes - go fetch it
+				WriteInlineTextL(aContext, code, pos, initialInlineWidth, ETrue, inlineFormat);
+				}
+
+			// Write a trailing ellipsis if this is the content-final chunk and is left_to_right
+			if (ellipsis_needed && direction == 0)
+				{
+				code.AppendByteL(TTmInterpreter::EOpInlineText);
+				code.AppendNumberL(iEllipsisWidth);
+				if (iEllipsisFormat >= line_start_position)
+					code.AppendNumberL(iEllipsisFormat - line_start_position);
+				else
+					code.AppendNumberL(line_start_position - iEllipsisFormat);
+				code.AppendNumberL(iEllipsis);
+				}
+
+			doc_pos = pos + length;
+			}
+
+		group_left = group_right;
+		if (ellipsis_needed)
+			group_left += iEllipsisWidth;
+		group_start = group_end;
+		}
+
+	// Write bytecode representing any control characters at the right end of
+	// the line, if the paragraph is left-to-right.
+	if (termination_chars > 0 && !aContext.iParFormat.RightToLeft())
+		{
+		TUint8 op = (TUint8)(TTmInterpreter::EOpText | TTmInterpreter::EModDontMeasure);
+		if (iHangingEndChar != doc_pos)
+			op |= TTmInterpreter::EModPos;
+		if (termination_chars != 1)
+			op |= TTmInterpreter::EModCount;
+		code.AppendByteL(op);
+		if (op & TTmInterpreter::EModPos)
+			code.AppendNumberL(iHangingEndChar - doc_pos);
+		if (op & TTmInterpreter::EModCount)
+			code.AppendNumberL(termination_chars);
+		code.AppendNumberL(0);
+		// Append context , which is NULL in this case
+		code.AppendNumberL(0);
+
+		}
+
+	// Scale the line dimensions if necessary.
+	TRect inner_bounds = iInnerBounds;
+	TInt ascent = iAscent;
+	TInt height = iHeight;
+	if (aContext.iXScale != 1000 || aContext.iYScale != 1000)
+		{
+		inner_bounds.iTl.iX = inner_bounds.iTl.iX * aContext.iXScale / 1000;
+		inner_bounds.iBr.iX = inner_bounds.iBr.iX * aContext.iXScale / 1000;
+		inner_bounds.iTl.iY = inner_bounds.iTl.iY * aContext.iYScale / 1000;
+		inner_bounds.iBr.iY = inner_bounds.iBr.iY * aContext.iYScale / 1000;
+		ascent = ascent * aContext.iYScale / 1000;
+
+		// Treat height specially to avoid cumulative rounding errors in the height of a document.
+		TInt line_top = aContext.iInfo.iHeight * aContext.iYScale / 1000;
+		TInt line_bottom = (aContext.iInfo.iHeight + iHeight) * aContext.iYScale / 1000;
+		height = line_bottom - line_top;
+		}
+
+	// Write the bytecode giving the line information.
+	TInt lineFlags = 0;
+	if (aContext.iLineEndsInForcedLineBreak)
+		lineFlags |= TTmInterpreter::ELineFlagLineEndsInForcedLineBreak;
+	else if (visualEndIsAmbiguous)
+		// line end cannot be ambiguous if there is a line break, even
+		// if the heuristics say it might be.
+		lineFlags |= TTmInterpreter::ELineFlagVisualEndOfLineIsAmbiguous;
+	TInt bytecode_size = code.Size() - line_start_bytecode_pos;
+	TUint8 op = TTmInterpreter::EOpLine;
+	if (aContext.iParFormat.RightToLeft())
+		op |= TTmInterpreter::EModRightToLeft;
+	if (iBordered)
+		op |= TTmInterpreter::EModBordered;
+	if (aContext.iLineInPar == 0)
+		op |= TTmInterpreter::EModParStart;
+	if (iAtParEnd)
+		op |= TTmInterpreter::EModParEnd;
+	if (lineFlags != 0)
+		op |= TTmInterpreter::EModLineFlags;
+	// Line info is inserted back at the beginning of this stretch of code
+	code.InsertByteL(op,line_start_bytecode_pos);
+	TInt pos = line_start_bytecode_pos + 1;
+	// <N> Number of characters in the line
+	pos = code.InsertNumberL(iNextLineStartChar - aContext.iInfo.iEndChar, pos);
+	// <C> Context  for the line
+	pos = code.InsertNumberL(lineContextChar, pos);
+	// <F> Line flags
+	if (lineFlags != 0)
+		pos = code.InsertNumberL(lineFlags, pos);
+	// <B> Size of the bytecode
+	pos = code.InsertNumberL(bytecode_size, pos);
+	// <H> Height of the line
+	pos = code.InsertNumberL(height, pos);
+	// <I> The inner bounds of the line, relative to the line's top-left corner
+	pos = code.InsertRectL(inner_bounds, pos);
+	// <A> The ascent: the distance between the baseline and the top of the line
+	pos = code.InsertNumberL(ascent, pos);
+	}
+
+void CTmLine::WriteInlineTextL(CTmFormatContext& aContext, CTmCode& aCode, TInt aPos, TInt aInlineWidth, TBool aLeadingEdge, TInt aInlineFormat)
+	{
+	MTmInlineTextSource* inlineTextApi = (MTmInlineTextSource*)aContext.iSource.GetExtendedInterface(KInlineTextApiExtensionUid);
+	__ASSERT_DEBUG(inlineTextApi!=0,TmPanic(EInvariant));
+
+	TTmDocPos startDocPos(aPos, aLeadingEdge);
+	TPtrC inlineText = inlineTextApi->GetInlineText(startDocPos);
+	TUint8 op2 = TTmInterpreter::EOpInlineText;
+	TInt inlineLength = inlineText.Length();
+	__ASSERT_DEBUG(inlineLength <= TTmInterpreter::EMaxInlineChars,TmPanic(EInvariant));
+	if (inlineLength > 1)
+		op2 |= TTmInterpreter::EModCount;
+	aCode.AppendByteL(op2);
+	if (inlineLength > 1)
+		aCode.AppendNumberL(inlineLength);
+	aCode.AppendNumberL(aInlineWidth);
+	aCode.AppendNumberL(aInlineFormat);
+	for (TInt i = 0; i < inlineLength; i++)
+		{
+		aCode.AppendNumberL(inlineText[i]);
+		}
+	}
+
+void CTmLine::WriteLabelCodeL(CTmFormatContext& aContext,MTmSource::TLabelType aLabelType)
+	{
+	// Format the label.
+	CTmCode *label_code = new(ELeave) CTmCode;
+	CleanupStack::PushL(label_code);
+	TTmFormatParam param;
+	param.iMaxLines = 1;	// labels have only one line
+	TSize label_size(0, 0);
+	TInt margin_size = 0;
+	MFormLabelApi* labelApi = (MFormLabelApi*)aContext.iSource.GetExtendedInterface(KFormLabelApiExtensionUid);
+	if (labelApi != NULL)
+		labelApi->LabelMetrics(aLabelType, label_size, margin_size);
+	param.iWrapWidth = label_size.iWidth;
+	CTmFormatContext::TInfo info;
+	info.iContextCharPerLine = NULL;
+	CTmFormatContext::FormatL(aContext.iSource,param,*label_code,info, aContext.iTextLayout);
+
+	// Make sure the main line's height and ascent are big enough to accommodate the label.
+	TTmInterpreterParam interpreter_param(*label_code);
+	interpreter_param.iWidth = info.iWidth;
+	TTmInterpreter interpreter(interpreter_param);
+	interpreter.LineNumberToLine(0);
+	int label_ascent = interpreter.LineInfo().iBaseline - interpreter.LineInfo().iOuterRect.iTl.iY;
+	int label_descent = interpreter.LineInfo().iOuterRect.Height() - label_ascent;
+	int line_descent = iHeight - iAscent;
+	iAscent = Max(iAscent,label_ascent);
+	iHeight = iAscent + Max(line_descent,label_descent);
+
+	// Insert the bytecode describing the label.
+	CTmCode& code = aContext.iCode;
+	code.AppendByteL(TTmInterpreter::EOpLabel | TTmInterpreter::EModCount);
+	code.AppendNumberL(0);	// a label consumes no bytes from the main text source
+	code.AppendNumberL(label_code->Size());
+	TRect r;
+	r.iTl.iX = -margin_size;
+	r.iBr.iX = r.iTl.iX + info.iWidth;
+	r.iBr.iY = iAscent + label_descent;
+	r.iTl.iY = r.iBr.iY - info.iHeight;
+	code.AppendRectL(r);
+	code.AppendNumberL(aLabelType);
+
+	// Insert the label bytecode.
+	code.ChangeL(code.Size(),code.Size(),*label_code);
+
+	// Clean up.
+	CleanupStack::PopAndDestroy();	// label_code
+	}
+
+void CTmLine::WriteBdStateL(CTmFormatContext& aContext)
+	{
+	int start_bytecode_pos = aContext.iCode.Size();
+	CTmCode& code = aContext.iCode;
+	RBufWriteStream stream(*code.Buffer(),start_bytecode_pos);
+	aContext.iBdState.ExternalizeL(stream);
+	aContext.iBdEndOfLine.ExternalizeL(stream);
+	stream.CommitL();
+	stream.Close();
+	int bytecode_size = code.Size() - start_bytecode_pos;
+	int pos = start_bytecode_pos;
+	code.InsertByteL(TTmInterpreter::EOpParam,pos++);
+	code.InsertByteL(TTmInterpreter::EParamBdState,pos++);
+	code.InsertNumberL(bytecode_size,pos);
+	}
+void CTmLine::SetContextChar(TUint aContext, TBool aFromChunk)
+	{
+	// If the last chunk in the line has context, then take it from there, else take it from the one provided.
+	if (aFromChunk && iChunk.Length() > 0)
+		iContextChar = iChunk[iChunk.Length() - 1].iContextChar;
+	else
+		iContextChar = aContext;
+	}